#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <psp2/kernel/processmgr.h>
#include <psp2/io/fcntl.h>

#include "list/file_list.h"
#include "activity/activities.h"
#include "setting/setting.h"
#include "emu/emu.h"
#include "gui/gui.h"
#include "utils.h"
#include "file.h"
#include "config.h"
#include "lang.h"

static int onStartActivity(GUI_Activity *activity);
static int onFinishActivity(GUI_Activity *activity);
static int onBeforeDrawActivity(GUI_Activity *activity);
static int onDrawActivity(GUI_Activity *activity);
static int onCtrlActivity(GUI_Activity *activity);

static GUI_ButtonInstruction button_instructions[] = {
    {LANG_LOCAL_BUTTON_CANCEL, LANG_PARENT_DIR, 1},
    {LANG_LOCAL_BUTTON_ENTER, LANG_OPEN_DIR, 1},
    {LANG_LOCAL_BUTTON_Y, LANG_CONTEXT_MENU, 1},
    {LANG_LOCAL_BUTTON_HOME, LANG_SETTING_MENU, 1},
    {LANG_LOCAL_BUTTON_SELECT, LANG_ABOUT, 1},
    {LANG_LOCAL_BUTTON_START, LANG_CHANGE_DIR, 1},
    {LANG_NULL, LANG_NULL, 0},
};

GUI_Activity browser_activity = {
    LANG_APP_TITLE,       // Title
    button_instructions,  // Button instructions
    NULL,                 // Wallpaper
    0,                    // Disable draw statusbar
    1,                    // Disable home event
    onStartActivity,      // Start callback
    onFinishActivity,     // Finish callback
    onBeforeDrawActivity, // Before draw callback
    onDrawActivity,       // Draw callback
    NULL,                 // After draw callback
    onCtrlActivity,       // Ctrl callback
    NULL,                 // Event callback
    NULL,                 // User data
};

enum ContextMenuIndex
{
    INDEX_CONTEXT_MENU_LOAD_GAME,
    INDEX_CONTEXT_MENU_DELETE_GAME,
    INDEX_CONTEXT_MENU_DELETE_AUTO_SAVESTATE,
    INDEX_CONTEXT_MENU_DELETE_AUTO_SAVEFILE,
    INDEX_CONTEXT_MENU_DELETE_CACHE_FILES,
};

static int context_menu_items[] = {
    LANG_CONTEXT_MENU_START_GAME,
    LANG_CONTEXT_MENU_DELETE_GAME,
    LANG_CONTEXT_MENU_DELETE_AUTO_SAVESTATE,
    LANG_CONTEXT_MENU_DELETE_AUTO_SAVEFILE,
    LANG_CONTEXT_MENU_DELETE_CACHE_FILES,
};
#define N_CONTEXT_MENU_ITEMS (sizeof(context_menu_items) / sizeof(int))

#define MAX_DIR_LEVELS 128

#define LAYOUT_CHILD_MARGIN 2

#define PATH_VIEW_PADDING_L 10
#define PATH_VIEW_PADDING_T 5
#define PATH_TEXT_COLOR COLOR_SPRING_GREEN

#define LISTVIEW_PADDING_L 2
#define LISTVIEW_PADDING_T 2
#define ITEMVIEW_PADDING_L 8
#define ITEMVIEW_PADDING_T 5

#define ITEMVIEW_COLOR_BG 0
#define ITEMVIEW_COLOR_FOCUS GUI_DEF_COLOR_FOCUS
#define FOLDER_TEXT_COLOR COLOR_WHITE
#define FILE_TEXT_COLOR COLOR_GREEN

#define PREVIEW_VIEW_PADDING 8
#define MAX_PREVIEW_TEXTURES 2

// Thread

static SceKernelLwMutexWork browser_mutex = {0};
static SceUID browser_thid = -1;
static int browser_run = 0;

// File list
static LinkedList *file_list = NULL;
static int list_sort_mode = SORT_BY_NAME;

// Position
static int focus_pos_saves[MAX_DIR_LEVELS] = {0};
static int dir_level = 0;

// Preview
static int preview_need_refresh = 1;
static int preview_refresh_wait = 0;
static GUI_Texture *preview_textures[MAX_PREVIEW_TEXTURES] = {0};
static int preview_texture_front = 0;
static int preview_texture_back = 1;
static uint32_t preview_texture_front_color = 0x0FFFFFFF;
static uint32_t preview_texture_back_color = 0x00FFFFFF;

// Layout
static Layout *browser_layout = NULL;
static TextView *path_textview = NULL;
static ListView *file_listview = NULL;
static ImageView *preview_imageview = NULL;

static uint32_t getGradualColor(uint32_t cur_color, int target_alpha, int step_alpha)
{
    int cur_alpha = COLOR_GET_ALPHA(cur_color);

    if (cur_alpha == target_alpha)
        return cur_color;

    if (step_alpha < 1)
        step_alpha = 1;

    if (cur_alpha < target_alpha)
    {
        cur_alpha += step_alpha;
        if (cur_alpha > target_alpha)
            cur_alpha = target_alpha;
    }
    else if (cur_alpha > target_alpha)
    {
        cur_alpha -= step_alpha;
        if (cur_alpha < target_alpha)
            cur_alpha = target_alpha;
    }

    return COLOR_SET_ALPHA(cur_color, cur_alpha);
}

static int refreshFileListView()
{
    ItemView *itemView;
    LinkedListEntry *entry;
    FileEntryData *data;
    int n;

    if (!file_listview || !file_list)
        return -1;

    ListViewEmptyItemViews(file_listview);

    entry = LinkedListHead(file_list);
    n = 0;
    while (entry)
    {
        itemView = NewItemView();
        if (!itemView)
            break;
        data = (FileEntryData *)LinkedListGetEntryData((LinkedListEntry *)entry);

        ItemViewSetLayoutSize(itemView, TYPE_LAYOUT_SIZE_MATH_PARENT, TYPE_LAYOUT_SIZE_WRAP_CONTENT);
        ItemViewSetPadding(itemView, ITEMVIEW_PADDING_L, ITEMVIEW_PADDING_L, ITEMVIEW_PADDING_T, ITEMVIEW_PADDING_T);
        ItemViewSetNameText(itemView, data->name);
        ItemViewSetNameTextColor(itemView, data->is_folder ? FOLDER_TEXT_COLOR : FILE_TEXT_COLOR);
        ItemViewSetNameTextScollEnabled(itemView, 1);
        ItemViewSetUserData(itemView, entry);
        ListViewAddItemView(file_listview, itemView);

        entry = LinkedListNext(entry);
        ++n;
    }

    ListViewMeasure(file_listview);

    return n;
}

static int createContentView()
{
    int layout_x = 0, layout_y = 0;
    int available_w = 0, available_h = 0;

    if (browser_layout)
        return 0;

    // 获取主布局的可用尺寸和位置
    GUI_GetActivityLayoutPosition(&browser_activity, &layout_x, &layout_y);
    GUI_GetActivityAvailableSize(&browser_activity, &available_w, &available_h);

    // 主布局
    browser_layout = NewLayout();
    if (!browser_layout)
        return -1;
    LayoutSetLayoutSize(browser_layout, TYPE_LAYOUT_SIZE_MATH_PARENT, TYPE_LAYOUT_SIZE_MATH_PARENT);
    LayoutSetLayoutPosition(browser_layout, layout_x, layout_y);
    LayoutSetLayoutAvailableSize(browser_layout, available_w, available_h);
    LayoutSetOrientation(browser_layout, TYPE_LAYOUT_ORIENTATION_VERTICAL);
    LayoutSetPadding(browser_layout, GUI_DEF_MAIN_LAYOUT_PADDING, GUI_DEF_MAIN_LAYOUT_PADDING, GUI_DEF_MAIN_LAYOUT_PADDING, GUI_DEF_MAIN_LAYOUT_PADDING);

    // 文件列表父路径
    path_textview = NewTextView();
    if (!path_textview)
        return -1;
    TextViewSetLayoutSize(path_textview, TYPE_LAYOUT_SIZE_MATH_PARENT, GUI_GetLineHeight() + PATH_VIEW_PADDING_T * 2);
    TextViewSetPadding(path_textview, PATH_VIEW_PADDING_L, PATH_VIEW_PADDING_L, PATH_VIEW_PADDING_T, PATH_VIEW_PADDING_T);
    TextViewSetBgColor(path_textview, GUI_DEF_COLOR_BG);
    TextViewSetSingleLine(path_textview, 1);
    TextViewSetTextColor(path_textview, PATH_TEXT_COLOR);
    TextViewSetText(path_textview, "");
    LayoutAddView(browser_layout, path_textview);

    // 文件列表和预览图布局
    Layout *layout2 = NewLayout();
    LayoutSetLayoutSize(layout2, TYPE_LAYOUT_SIZE_MATH_PARENT, TYPE_LAYOUT_SIZE_MATH_PARENT);
    LayoutSetLayoutMargin(layout2, 0, 0, LAYOUT_CHILD_MARGIN, 0);
    LayoutSetOrientation(layout2, TYPE_LAYOUT_ORIENTATION_HORIZONTAL);
    LayoutAddView(browser_layout, layout2);

    // 先更新主布局以获取layout2的可用尺寸用来确定预览图的视图尺寸
    LayoutMeasure(browser_layout);
    int preview_Measured_h = 0;
    LayoutGetMeasuredContentSize(layout2, NULL, &preview_Measured_h);

    // 预览图
    preview_imageview = NewImageView();
    if (!preview_imageview)
        return -1;
    ImageViewSetLayoutSize(preview_imageview, preview_Measured_h, preview_Measured_h);
    ImageViewSetPadding(preview_imageview, PREVIEW_VIEW_PADDING, PREVIEW_VIEW_PADDING, PREVIEW_VIEW_PADDING, PREVIEW_VIEW_PADDING);
    ImageViewSetBgColor(preview_imageview, GUI_DEF_COLOR_BG);
    LayoutAddView(layout2, preview_imageview);

    // 文件列表
    file_listview = NewListView();
    if (!file_listview)
        return -1;
    ListViewSetLayoutSize(file_listview, TYPE_LAYOUT_SIZE_MATH_PARENT, TYPE_LAYOUT_SIZE_MATH_PARENT);
    ListViewSetLayoutMargin(file_listview, 0, LAYOUT_CHILD_MARGIN, 0, 0);
    ListViewSetOrientation(file_listview, TYPE_LAYOUT_ORIENTATION_VERTICAL);
    ListViewSetPadding(file_listview, LISTVIEW_PADDING_L, LISTVIEW_PADDING_L, LISTVIEW_PADDING_T, LISTVIEW_PADDING_T);
    ListViewSetBgColor(file_listview, GUI_DEF_COLOR_BG);
    ListViewSetCursorEnabled(file_listview, 1);
    ListViewSetCursorColor(file_listview, ITEMVIEW_COLOR_FOCUS);
    LayoutAddViewAbove(layout2, file_listview, preview_imageview);

    // 更新文件列表和预览图布局
    LayoutMeasure(layout2);

    return 0;
}

static int destroyContentView()
{
    LayoutDestroy(browser_layout);
    browser_layout = NULL;
    path_textview = NULL;
    preview_imageview = NULL;
    file_listview = NULL;

    return 0;
}

int Browser_CurrentPathIsGame()
{
    int focus_pos = ListViewGetFocusPos(file_listview);
    LinkedListEntry *entry = LinkedListFindByNum(file_list, focus_pos);
    FileEntryData *data = (FileEntryData *)LinkedListGetEntryData(entry);
    if (data)
        return !data->is_folder;

    return 0;
}

int Browser_MakeCurrentFileName(char *name)
{
    int focus_pos = ListViewGetFocusPos(file_listview);
    LinkedListEntry *entry = LinkedListFindByNum(file_list, focus_pos);
    FileEntryData *data = (FileEntryData *)LinkedListGetEntryData(entry);
    if (!data || !data->name)
        return -1;

    snprintf(name, MAX_NAME_LENGTH, "%s", data->name);
    return 0;
}

int Browser_MakeCurrentFilePath(char *path)
{
    FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);
    int focus_pos = ListViewGetFocusPos(file_listview);
    LinkedListEntry *entry = LinkedListFindByNum(file_list, focus_pos);
    FileEntryData *e_data = (FileEntryData *)LinkedListGetEntryData(entry);
    if (!ls_data || !e_data || !e_data->name)
        return -1;

    snprintf(path, MAX_PATH_LENGTH, "%s%s", ls_data->path, e_data->name);
    return 0;
}

GUI_Texture *GetDefaultPreviewTexture()
{
    char path[MAX_PATH_LENGTH];
    MakePreviewPath(path);
    return GUI_LoadPNGFile(path);
}

static void updatePreviewImageView(GUI_Texture *texture)
{
    switch (app_config.preview_style)
    {
    case TYPE_PREVIEW_SCALE_TYPE_FIT_XY:
        ImageViewSetScaleType(preview_imageview, TYPE_IMAGE_SCALE_FIT_XY);
        break;
    case TYPE_PREVIEW_SCALE_TYPE_FIT_CENTER_CROP:
        ImageViewSetScaleType(preview_imageview, TYPE_IMAGE_SCALE_FIT_CENTER_CROP);
        break;
    case TYPE_PREVIEW_SCALE_TYPE_FIT_CENTER_INSIDE:
    default:
        ImageViewSetScaleType(preview_imageview, TYPE_IMAGE_SCALE_FIT_CENTER_INSIDE);
        break;
    }

    ImageViewSetTexture(preview_imageview, texture);
    ImageViewMeasure(preview_imageview);
}

static GUI_Texture *getPreviewTexture()
{
    GUI_Texture *texture = NULL;

    if (CurrentPathIsGame())
    {
        switch (app_config.preview_path)
        {
        case TYPE_PREVIEW_PATH_DEFAULT:
            texture = GetDefaultPreviewTexture();
            break;
        case TYPE_PREVIEW_PATH_SAVESTATE:
            texture = Emu_GetStateScreenshotTexture(-1);
            break;
        case TYPE_PREVIEW_PATH_AUTO:
        default:
            if (misc_config.auto_save_load)
                texture = Emu_GetStateScreenshotTexture(-1);
            if (!texture)
                texture = GetDefaultPreviewTexture();
            break;
        }
    }

    return texture;
}

static int incrementDirLevels(int pos)
{
    if (dir_level < MAX_DIR_LEVELS - 1)
    {
        focus_pos_saves[dir_level] = pos;
        dir_level++;
    }

    // 返回新光标位置
    return 0;
}

static int decrementDirLevels()
{
    if (!file_list)
        return -1;

    FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);
    RemoveEndSlash(ls_data->path);

    char *p;
    p = strrchr(ls_data->path, '/'); // ux0:abc/cde
    if (p)
    {
        p[1] = '\0';
        dir_level--;
        goto DIR_UP_RETURN;
    }

    p = strrchr(ls_data->path, ':'); // ux0:abc || ux0:
    if (p)
    {
        if (strlen(ls_data->path) - ((p + 1) - ls_data->path) > 0) // strlen(ux0:abc) - (p(:)+1 - p(u)) > 0 for skip ux0:
        {
            p[1] = '\0';
            dir_level--;
            goto DIR_UP_RETURN;
        }
    }

    strcpy(ls_data->path, HOME_PATH);
    dir_level = 0;

DIR_UP_RETURN:
    if (dir_level < 0)
        dir_level = 0;

    // 返回旧光标位置
    return focus_pos_saves[dir_level];
}

static int getFileListPosByName(const char *name)
{
    int length = LinkedListGetLength(file_list);
    int pos = FileListGetNumberByName(file_list, name);
    if (pos < 0 || pos > length - 1)
        return 0;

    return pos;
}

static int refreshFileList(int pos, int update_view)
{
    if (!file_list)
        return -1;

    // 刷新列表需要加锁，因为线程里在读取预览图需要用到列表信息
    sceKernelLockLwMutex(&browser_mutex, 1, NULL);

    int ret = 0, res = 0;
    int focus_pos = pos;
    FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);

    do
    {
        LinkedListEmpty(file_list);
        res = FileListGetEntries(file_list, ls_data->path, list_sort_mode);
        if (res < 0)
        {
            ret = res;
            focus_pos = decrementDirLevels();
        }
    } while (res < 0);

    if (update_view)
    {
        TextViewSetText(path_textview, ls_data->path);
        TextViewMeasure(path_textview);
        refreshFileListView();
        ListViewSetFocusPos(file_listview, focus_pos, 0);
        Browser_RequestRefreshPreview();
    }

    sceKernelUnlockLwMutex(&browser_mutex, 1);
    return ret;
}

static int changeToDir(char *lastdir)
{
    if (!file_list)
        return -1;

    if (!CheckFolderExist(lastdir))
        return -1;

    FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);
    dir_level = 0;
    strcpy(ls_data->path, HOME_PATH);

    int fcous_pos = 0;
    int i;
    for (i = 0; i < strlen(lastdir) + 1; i++)
    {
        if (lastdir[i] == ':' || lastdir[i] == '/')
        {
            char ch = lastdir[i + 1];
            lastdir[i + 1] = '\0';

            char ch2 = lastdir[i];
            lastdir[i] = '\0';

            char *p = strrchr(lastdir, '/');
            if (!p)
                p = strrchr(lastdir, ':');
            if (!p)
                p = lastdir - 1;

            lastdir[i] = ch2;

            refreshFileList(0, 0);
            fcous_pos = getFileListPosByName(p + 1);

            strcpy(ls_data->path, lastdir);

            lastdir[i + 1] = ch;

            incrementDirLevels(fcous_pos);
        }
    }
    refreshFileList(fcous_pos, 1);

    return 0;
}

static void backToParentDir()
{
    if (dir_level > 0)
    {
        refreshFileList(decrementDirLevels(), 1);
    }
}

static void enterToChildDir(LinkedListEntry *entry)
{
    FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);
    FileEntryData *e_data = (FileEntryData *)LinkedListGetEntryData(entry);
    if (!ls_data || !e_data)
        return;

    if (dir_level == 0)
    {
        strcpy(ls_data->path, e_data->name);
    }
    else
    {
        if (dir_level > 1)
            AddEndSlash(ls_data->path);
        strcat(ls_data->path, e_data->name);
    }

    refreshFileList(incrementDirLevels(ListViewGetFocusPos(file_listview)), 1);
}

static void startGame(LinkedListEntry *entry)
{
    FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);
    FileEntryData *e_data = (FileEntryData *)LinkedListGetEntryData(entry);
    if (!ls_data || !e_data)
        return;

    EmuGameInfo info;
    snprintf(info.path, MAX_PATH_LENGTH, "%s%s", ls_data->path, e_data->name);
    info.state_num = -2;
    Emu_StartGame(&info);
}

static int onDeleteGameClick(AlertDialog *dialog, int which)
{
    char path[MAX_PATH_LENGTH];
    if (MakeCurrentGamePath(path) < 0)
        return -1;

    sceIoRemove(path);
#ifdef FBA_BUILD
    FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);
    char base_name[MAX_NAME_LENGTH];
    MakeBaseName(base_name, path, MAX_NAME_LENGTH);
    snprintf(path, MAX_PATH_LENGTH, "%s%s.dat", ls_data->path, base_name);
    sceIoRemove(path);
#endif
    refreshFileList(ListViewGetFocusPos(file_listview), 1);
    Browser_RequestRefreshPreview();

    if (dialog)
    {
        AlertDialog *prev_dialog = AlertDialog_GetUserData(dialog);
        AlertDialog_Dismiss(prev_dialog);
        AlertDialog_Dismiss(dialog);
    }

    return 0;
}

static int onDeleteAutoSavestateClick(AlertDialog *dialog, int which)
{
    Emu_DeleteState(-1);
    Browser_RequestRefreshPreview();

    if (dialog)
    {
        AlertDialog *prev_dialog = AlertDialog_GetUserData(dialog);
        AlertDialog_Dismiss(prev_dialog);
        AlertDialog_Dismiss(dialog);
    }

    return 0;
}

static int onDeleteAutoSavefileClick(AlertDialog *dialog, int which)
{
    Emu_DeleteSram();

    if (dialog)
    {
        AlertDialog *prev_dialog = AlertDialog_GetUserData(dialog);
        AlertDialog_Dismiss(prev_dialog);
        AlertDialog_Dismiss(dialog);
    }

    return 0;
}

static int onDeleteCacheFilesClick(AlertDialog *dialog, int which)
{
#if defined(WANT_ARCHIVE_ROM)
    char path[MAX_PATH_LENGTH];
    MakeCurrentGamePath(path);
    Archive_CleanCacheByPath(path);
#endif

    if (dialog)
    {
        AlertDialog *prev_dialog = AlertDialog_GetUserData(dialog);
        AlertDialog_Dismiss(prev_dialog);
        AlertDialog_Dismiss(dialog);
    }

    return 0;
}

static int onContextMenuItemClick(AlertDialog *dialog, int which)
{
    if (!dialog)
        return -1;

    int focus_pos = ListViewGetFocusPos(file_listview);

    switch (which)
    {
    case INDEX_CONTEXT_MENU_LOAD_GAME:
    {
        LinkedListEntry *entry = LinkedListFindByNum(file_list, focus_pos);
        FileEntryData *data = (FileEntryData *)LinkedListGetEntryData(entry);
        if (data && !data->is_folder)
        {
            AlertDialog_Close(dialog);
            startGame(entry);
        }
        break;
    }
    case INDEX_CONTEXT_MENU_DELETE_GAME:
    {
        if (CurrentPathIsGame())
        {
            AlertDialog *ask_dialog = AlertDialog_Create();
            AlertDialog_SetTitle(ask_dialog, cur_lang[LANG_TIP]);
            AlertDialog_SetMessage(ask_dialog, cur_lang[LANG_MESSAGE_ASK_DELETE_GAME]);
            AlertDialog_SetPositiveButton(ask_dialog, cur_lang[LANG_CONFIRM], onDeleteGameClick);
            AlertDialog_SetNegativeButton(ask_dialog, cur_lang[LANG_CANCEL], AlertDialog_OnClickDismiss);
            AlertDialog_SetUserData(ask_dialog, dialog);
            AlertDialog_Show(ask_dialog);
        }
        break;
    }
    case INDEX_CONTEXT_MENU_DELETE_AUTO_SAVESTATE:
    {
        if (CurrentPathIsGame())
        {
            AlertDialog *ask_dialog = AlertDialog_Create();
            AlertDialog_SetTitle(ask_dialog, cur_lang[LANG_TIP]);
            AlertDialog_SetMessage(ask_dialog, cur_lang[LANG_MESSAGE_ASK_DELETE_AUTO_STATE]);
            AlertDialog_SetPositiveButton(ask_dialog, cur_lang[LANG_CONFIRM], onDeleteAutoSavestateClick);
            AlertDialog_SetNegativeButton(ask_dialog, cur_lang[LANG_CANCEL], AlertDialog_OnClickDismiss);
            AlertDialog_SetUserData(ask_dialog, dialog);
            AlertDialog_Show(ask_dialog);
        }
        break;
    }
    case INDEX_CONTEXT_MENU_DELETE_AUTO_SAVEFILE:
    {
        if (CurrentPathIsGame())
        {
            AlertDialog *ask_dialog = AlertDialog_Create();
            AlertDialog_SetTitle(ask_dialog, cur_lang[LANG_TIP]);
            AlertDialog_SetMessage(ask_dialog, cur_lang[LANG_MESSAGE_ASK_DELETE_AUTO_SAVEFILE]);
            AlertDialog_SetPositiveButton(ask_dialog, cur_lang[LANG_CONFIRM], onDeleteAutoSavefileClick);
            AlertDialog_SetNegativeButton(ask_dialog, cur_lang[LANG_CANCEL], AlertDialog_OnClickDismiss);
            AlertDialog_SetUserData(ask_dialog, dialog);
            AlertDialog_Show(ask_dialog);
        }
        break;
    }
    case INDEX_CONTEXT_MENU_DELETE_CACHE_FILES:
    {
        if (CurrentPathIsGame())
        {
            AlertDialog *ask_dialog = AlertDialog_Create();
            AlertDialog_SetTitle(ask_dialog, cur_lang[LANG_TIP]);
            AlertDialog_SetMessage(ask_dialog, cur_lang[LANG_MESSAGE_ASK_DELETE_CACHE_FILES]);
            AlertDialog_SetPositiveButton(ask_dialog, cur_lang[LANG_CONFIRM], onDeleteCacheFilesClick);
            AlertDialog_SetNegativeButton(ask_dialog, cur_lang[LANG_CANCEL], AlertDialog_OnClickDismiss);
            AlertDialog_SetUserData(ask_dialog, dialog);
            AlertDialog_Show(ask_dialog);
        }
        break;
    }
    default:
        break;
    }

    return 0;
}

static void openContextMenu()
{
    int focus_pos = ListViewGetFocusPos(file_listview);
    LinkedListEntry *entry = LinkedListFindByNum(file_list, focus_pos);
    FileEntryData *data = (FileEntryData *)LinkedListGetEntryData(entry);
    if (!data || data->is_folder)
        return;

    AlertDialog *dialog = AlertDialog_Create();
    AlertDialog_SetTitle(dialog, cur_lang[LANG_MENU]);
    int n_items = N_CONTEXT_MENU_ITEMS;
    const char **items = GetStringArrayByLangArray(context_menu_items, n_items);
    AlertDialog_SetItems(dialog, (const char *const *)items, n_items);
    AlertDialog_SetPositiveButton(dialog, cur_lang[LANG_CONFIRM], onContextMenuItemClick);
    AlertDialog_SetNegativeButton(dialog, cur_lang[LANG_CANCEL], AlertDialog_OnClickDismiss);
    AlertDialog_Show(dialog);
    if (items)
        free(items);
}

static int browserThreadEntry(SceSize args, void *argp)
{
    int old_dir_level = -1;
    int old_focus_pos = -1;
    int focus_pos = 0;
    int focus_changed = 0;
    GUI_Texture *texture = NULL;
    int cur_path_is_game = 0;

    while (browser_run)
    {
        // 获取列表信息需要加锁，因为主线程有可能在刷新列表，不加锁会导致崩溃
        sceKernelLockLwMutex(&browser_mutex, 1, NULL);

        focus_pos = ListViewGetFocusPos(file_listview);
        focus_changed = (old_dir_level != dir_level || old_focus_pos != focus_pos);

        if (preview_refresh_wait || (!preview_need_refresh && !focus_changed))
        {
            sceKernelUnlockLwMutex(&browser_mutex, 1);
            sceKernelDelayThread(1000);
            continue;
        }

        cur_path_is_game = CurrentPathIsGame();
        texture = getPreviewTexture();

        sceKernelUnlockLwMutex(&browser_mutex, 1);

        GUI_LockDrawMutex();

        preview_need_refresh = 0;
        old_dir_level = dir_level;
        old_focus_pos = focus_pos;

        if (preview_textures[preview_texture_front]) // 更新新旧预览图位置
        {
            preview_texture_back = preview_texture_front;
            preview_texture_back_color = preview_texture_front_color;
            preview_texture_front = (preview_texture_front + 1) % MAX_PREVIEW_TEXTURES;
            preview_texture_front_color = 0x0FFFFFFF;
            if (preview_textures[preview_texture_front]) // 新的预览图位置上若有数据，将其释放
            {
                GUI_DestroyTexture(preview_textures[preview_texture_front]);
                preview_textures[preview_texture_front] = NULL;
            }
        }

        if (texture) // 如果读取新预览图成功，将新预览图更新到控件上
        {
            preview_textures[preview_texture_front] = texture;
            updatePreviewImageView(preview_textures[preview_texture_front]);
        }

        if (focus_changed)
            Setting_SetStateSelectId(0);

        if (cur_path_is_game)
        {
            button_instructions[1].instruction = LANG_START_GAME;
            button_instructions[2].visibility = 1;
        }
        else
        {
            button_instructions[1].instruction = LANG_OPEN_DIR;
            button_instructions[2].visibility = 0;
        }

        GUI_UnlockDrawMutex();
    }

    int i;
    GUI_LockDrawMutex();
    for (i = 0; i < MAX_PREVIEW_TEXTURES; i++)
    {
        GUI_DestroyTexture(preview_textures[i]);
        preview_textures[i] = NULL;
    }
    GUI_UnlockDrawMutex();

    sceKernelExitThread(0);
    return 0;
}

static int startBrowserThread()
{
    int ret = 0;

    if (browser_thid < 0)
        ret = browser_thid = sceKernelCreateThread("browser_thread", browserThreadEntry, 0x10000100, 0x40000, 0, 0, NULL);
    if (browser_thid >= 0)
    {
        browser_run = 1;
        ret = sceKernelStartThread(browser_thid, 0, NULL);
        if (ret < 0)
        {
            browser_run = 0;
            sceKernelDeleteThread(browser_thid);
            browser_thid = -1;
        }
    }

    return ret;
}

static int finishBrowserThread()
{
    browser_run = 0;
    if (browser_thid >= 0)
    {
        sceKernelWaitThreadEnd(browser_thid, NULL, NULL);
        sceKernelDeleteThread(browser_thid);
        browser_thid = -1;
    }

    return 0;
}

static void onFileListViewItemClick(ListView *listView, void *itemView, int id)
{
    LinkedListEntry *entry;
    if (itemView)
        entry = (LinkedListEntry *)ItemViewGetUserData((ItemView *)itemView);
    else
        entry = LinkedListFindByNum(file_list, id);
    FileEntryData *data = (FileEntryData *)LinkedListGetEntryData(entry);
    if (data)
    {
        if (data->is_folder)
            enterToChildDir(entry);
        else
            startGame(entry);
    }
}

static int onStartActivity(GUI_Activity *activity)
{
    browser_activity.wallpaper = GUI_GetImage(ID_GUI_IMAGE_WALLPAPER);

    // 先创建file_list，createLayout要用到
    file_list = NewFileList();
    if (!file_list)
        return -1;

    // 创建线程锁
    sceKernelCreateLwMutex(&browser_mutex, "browser_mutex", 2, 0, NULL);

    // 创建Activity视图
    createContentView();

    // 跳转到最后游玩的游戏位置
    if (Browser_ChangeDirBySaveFile(LASTFILE_PATH) < 0)
    {
        // 如果失败打开home目录列表
        FileListData *ls_data = (FileListData *)LinkedListGetListData(file_list);
        strcpy(ls_data->path, HOME_PATH);
        refreshFileList(0, 1);
    }

    // 创建线程
    startBrowserThread();

    return 0;
}

static int onFinishActivity(GUI_Activity *activity)
{
    finishBrowserThread();
    destroyContentView();
    sceKernelDeleteLwMutex(&browser_mutex);
    LinkedListDestroy(file_list);
    file_list = NULL;
    return 0;
}

static int onBeforeDrawActivity(GUI_Activity *activity)
{
    GUI_LockDrawMutex();

    if (!browser_activity.wallpaper)
        browser_activity.wallpaper = GUI_GetImage(ID_GUI_IMAGE_WALLPAPER);

    const GUI_Texture *cur_preview_tex = ImageViewGetTexture(preview_imageview);
    if (cur_preview_tex)
    {
        if (cur_preview_tex == preview_textures[preview_texture_back]) // 旧预览图
        {
            if (preview_texture_back_color != 0x00FFFFFF) // 逐渐透明
            {
                preview_texture_back_color = getGradualColor(preview_texture_back_color, 0, 0xFF / 10);
                ImageViewSetTintColor(preview_imageview, preview_texture_back_color);
            }
        }
        else if (cur_preview_tex == preview_textures[preview_texture_front]) // 当前预览图
        {
            if (preview_texture_front_color != 0xFFFFFFFF) // 逐渐显现
            {
                preview_texture_front_color = getGradualColor(preview_texture_front_color, 0xFF, 0xFF / 10);
                ImageViewSetTintColor(preview_imageview, preview_texture_front_color);
            }
        }
    }

    GUI_UnlockDrawMutex();

    return 0;
}

static int onDrawActivity(GUI_Activity *activity)
{
    LayoutDraw(browser_layout);
    return 0;
}

static int onCtrlActivity(GUI_Activity *activity)
{
    if (hold_pad[PAD_UP] || hold2_pad[PAD_LEFT_ANALOG_UP])
    {
        ListViewMoveFocusPos(file_listview, TYPE_MOVE_UP, 1);
    }
    else if (hold_pad[PAD_DOWN] || hold2_pad[PAD_LEFT_ANALOG_DOWN])
    {
        ListViewMoveFocusPos(file_listview, TYPE_MOVE_DOWN, 1);
    }
    else if (hold_pad[PAD_LEFT])
    {
        ListViewMoveFocusPos(file_listview, TYPE_MOVE_LEFT, 1);
    }
    else if (hold_pad[PAD_RIGHT])
    {
        ListViewMoveFocusPos(file_listview, TYPE_MOVE_RIGHT, 1);
    }
    else if (released_pad[PAD_Y])
    {
        openContextMenu();
    }
    else if (released_pad[PAD_CANCEL])
    {
        backToParentDir();
    }
    else if (released_pad[PAD_ENTER])
    {
        onFileListViewItemClick(file_listview, NULL, ListViewGetFocusPos(file_listview));
    }
    else if (released_pad[PAD_SELECT])
    {
        GUI_StartActivity(&about_activity);
    }
    else if (released_pad[PAD_START])
    {
        Browser_ChangeDirBySaveFile(LASTFILE_PATH);
    }
    else if (released_pad[PAD_HOME])
    {
        if (GUI_IsHomeKeyEnabled())
            Setting_OpenMenu();
    }

    return 0;
}

void Browser_RequestRefreshPreview()
{
    preview_need_refresh = 1;
}

int Browser_ChangeDirByFilePath(const char *path)
{
    int ret = 0;
    preview_refresh_wait = 1;

    char lastdir[MAX_PATH_LENGTH];
    if (MakeParentDir(lastdir, path, MAX_PATH_LENGTH) < 0)
        goto FAILED;

    if (changeToDir(lastdir) < 0)
        goto FAILED;

    char name[MAX_NAME_LENGTH];
    if (MakeFileName(name, path, MAX_NAME_LENGTH) >= 0)
        ListViewSetFocusPos(file_listview, getFileListPosByName(name), 1);

END:
    preview_refresh_wait = 0;
    return ret;

FAILED:
    ret = -1;
    goto END;
}

int Browser_ChangeDirBySaveFile(const char *path)
{
    char lastfile[MAX_PATH_LENGTH];
    if (ReadFile(path, lastfile, sizeof(lastfile)) <= 0)
        return -1;
    lastfile[MAX_PATH_LENGTH - 1] = '\0';

    return Browser_ChangeDirByFilePath(lastfile);
}
